package org.iori.pdf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

public class ContinueFTP {
	
	//UploadStatusコード 
	public enum UploadStatus {
		 Create_Directory_Fail,   //サーバーでフォルダを作成することが失敗した場合
		 Create_Directory_Success, //サーバーでフォルダを作成することが成功した場合
		 Upload_New_File_Success, //追加ファイルをサーバーにアップロードすることが成功した場合
		 Upload_New_File_Failed,   //追加ファイルをサーバーにアップロードすることが失敗した場合
		 File_Exits,      //ファイルが存在している場合
		 Remote_Bigger_Local,   //サーバーのファイルより、ローカルのファイルが小さい場合
		 Upload_From_Break_Success, //ｆｔｐレジュームすることが成功した場合
		 Upload_From_Break_Failed, //ｆｔｐレジュームすることが失敗した場合
		 Delete_Remote_Faild;   //サーバーでファイルを削除することが失敗した場合
	 }

	private FTPClient ftpClient = new FTPClient();

	public ContinueFTP() {
		//実行するコマンドをConsoleで表示する
		this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
	}

	/**
	* ftpサーバーに登録する
	* @param hostname ftpサーバーの名前
	* @param port ポート
	* @param username 登録ユーザ
	* @param password パスワード
	* @return 成功するかどうか
	* @throws IOException
	*/
	public boolean connect(String hostname,int port,String username,String password) throws IOException{
		ftpClient.connect(hostname, port);
		ftpClient.setControlEncoding("SJIS");
		if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
			if(ftpClient.login(username, password)) {
				System.out.println("========ftpログインOK==========");
				return true;
			} else {
				System.out.println("========ftpログインNG==========");
			}
		} else {
			System.out.println("========ftp接続NG==========");
		}
		disconnect();
		return false;
	}
	
	/**
	* ftpサーバーからダウンロードする
	* @param remote サーバーのパス
	* @param local ローカルのパス
	* @return 成功するかどうか
	* @throws IOException
	*/
	public boolean download(String remote,String local) throws IOException{
		ftpClient.enterLocalPassiveMode();
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
		boolean result;
		File f = new File(local);
		FTPFile[] files = ftpClient.listFiles(remote);
		if(files.length != 1) {
			System.out.println("サーバーのパスの配下で多くファイルがある");
			return false;
		}
		long lRemoteSize = files[0].getSize();
		if(f.exists()) {
			OutputStream out = new FileOutputStream(f,true);
			System.out.println("ローカルファイルのサイズ:"+f.length());
			if(f.length() >= lRemoteSize) {
				System.out.println("サーバーのファイルより、ローカルのファイルのサイズが大きい場合、ダウンロードすることを止める");
				return false;
			}
			ftpClient.setRestartOffset(f.length());
			result = ftpClient.retrieveFile(remote, out);
			out.close();
		} else {
			OutputStream out = new FileOutputStream(f);
			result = ftpClient.retrieveFile(remote, out);
			out.close();
		}
		return result;
	}

	/**
	* ftpサーバーにアップロードする(ｆｔｐレジューム対応)
	* @param local ローカルのパス
	* @param remote サーバーのパス
	* @return 成功するかどうか
	* @throws IOException
	*/
	public UploadStatus upload(String local,String remote) throws IOException{
		//PassiveModeに設定
		ftpClient.enterLocalPassiveMode();
		//バイナリモードに設定
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
		UploadStatus result;
		//始めてサーバーのフォルダに操作する
		String remoteFileName = remote;
		if(remote.contains("/")) {
			remoteFileName = remote.substring(remote.lastIndexOf("/")+1);
			String directory = remote.substring(0,remote.lastIndexOf("/")+1);
			if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(directory)) {
				//サーバーのフォルダが存在しない場合、サーバーでフォルダを作成する
				int start=0;
				int end = 0;
				if(directory.startsWith("/")) {
					start = 1;
				} else {
					start = 0;
				}
				end = directory.indexOf("/",start);
				while(true) {
					String subDirectory = remote.substring(start,end);
					if(!ftpClient.changeWorkingDirectory(subDirectory)) {
						if(ftpClient.makeDirectory(subDirectory)) {
							ftpClient.changeWorkingDirectory(subDirectory);
						} else {
							System.out.println("サーバーでフォルダを作成することが失敗だ");
							return UploadStatus.Create_Directory_Fail;
						}
					}
					start = end + 1;
					end = directory.indexOf("/",start);
					//サーバーでフォルダの作成が終わるかどうか、チェックする
					if(end <= start) {
						break;
					}
				}
			}
		}
		//サーバーでフォルダを存在するかどうか、チェックする
		FTPFile[] files = ftpClient.listFiles(remoteFileName);
		if(files.length == 1) {
			long remoteSize = files[0].getSize();
			File f = new File(local);
			long localSize = f.length();
			if(remoteSize==localSize) {
				return UploadStatus.File_Exits;
			} else if (remoteSize > localSize) {
				return UploadStatus.Remote_Bigger_Local;
			}
			//レジュームの対応
			InputStream is = new FileInputStream(f);
			if(is.skip(remoteSize)==remoteSize) {
				ftpClient.setRestartOffset(remoteSize);
			if(ftpClient.storeFile(remote, is)) {
				return UploadStatus.Upload_From_Break_Success;
			}
		}
		//レジュームできない場合、ファイルを削除して再アップロードする
		if(!ftpClient.deleteFile(remoteFileName)) {
			return UploadStatus.Delete_Remote_Faild;
		}
		is = new FileInputStream(f);
		if(ftpClient.storeFile(remote, is)) {
			result = UploadStatus.Upload_New_File_Success;
		} else {
			result = UploadStatus.Upload_New_File_Failed;
		}
		is.close();
		} else {
			InputStream is = new FileInputStream(local);
			if(ftpClient.storeFile(remoteFileName, is)) {
					result = UploadStatus.Upload_New_File_Success;
			} else {
				result = UploadStatus.Upload_New_File_Failed;
			}
			is.close();
		}
		return result;
	}

	/**
	* ftpサーバーにログアウトする
	* @throws IOException
	*/
	public void disconnect() throws IOException{
		if(ftpClient.isConnected()) {
			ftpClient.disconnect();
		}
	}

	private static String up_path = "C:/TDDOWNLOAD/";
	private static String ftp_path = "/home/matrix/";
	private static String up_file = "X16-42552VS2010UltimTrial1.iso";
	private static String ftp_file = "X16-42552VS2010UltimTrial1.iso";
	
	public static void main(String[] args) {
		ContinueFTP myFtp = new ContinueFTP();
		try {
			myFtp.connect("192.168.3.37", 21, "matrix", "matrix");
			System.out.println(myFtp.upload(up_path + up_file, ftp_path + ftp_file));
			myFtp.disconnect();
		} catch (IOException e) {
			System.out.println("ftpサーバーに接続できない："+e.getMessage());
		}
	}
}
